'''
英式蛮力法寻找易位词，思路：
1.记载字典文件
2.创建一个方法统计与name长度相等的单词列表
3.创建一个方法使用cv过滤法排除不合格的单词
4.创建一个方法将name从合格的cv过滤项目中再过滤
5.创建一个方法，将cv的过滤项目中再进行三元过滤
6.创建一个方法将三元过滤的结果再进行二元过滤
7.创建一个方法让用户选择开头字母
8.创建mian函数执行所有方法
'''
import sys,load
from collections import Counter
from itertools import permutations

'''
    #2.统计name与单词长度
            打印原始单词列表长度
            列表推导 = 判断单词长度是否与name相等 如果是从单词字典中遍历出单词，将单词小写化
            打印现在单词列表长度
            返回这个单词列表
'''

def prep_words(name,word_list_ini):
    print('将从{}个单词中寻找'.format(len(word_list_ini)))
    len_name = len(name)
    word_list = [word.lower() for word in word_list_ini if len_name == len(word)]
    print('已从中筛选出与{}相近的{}个单词'.format(name,len(word_list)),file=sys.stderr)
    return word_list

'''
#3.使用cv过滤法排除不合格的单词
        定义所有元音字母
        创建一个列表以存放筛选结果
        遍历出#2筛选出的所有单词，转换成c-v格式，保存进列表
        cv单词模式唯一化
        设定排除百分比
        定义余下合格的cv单词数量
        筛选出列表中出现频率与之最接近的单词
        创建一个空集合确定单词唯一化
        遍历错有最接近的单词并添加到集合中
'''

def cv_map_words(word_list):
    vowels = 'aeiou'
    cv_mapped_words = []
    for word in word_list:
        temp = ''
        for letter in word:
            if letter in vowels:
                temp += 'v' #元音字母变v
            else:
                temp += 'c'
        cv_mapped_words.append(temp)
    total = len(set(cv_mapped_words))
    target = 0.05
    num = int(total*target)
    count_pruned = Counter(cv_mapped_words).most_common(total-num)
    filtered_cv_map = set()
    for pattern,count in count_pruned:
        filtered_cv_map.add(pattern)
    print('当前筛选出合格的cv模式单词{}个'.format(len(filtered_cv_map)),file=sys.stderr)
    return filtered_cv_map

'''
#4.cv模式化name与#3作比较
        列表推导(字典) = 遍历出name值所有字母能排列出单单词
        定义所有元音字母
        创建空集合用于存放结果
        遍历name值的字母排列
            遍历错有排列字母转化为cv模式并保存
'''

def cv_map_filter(name,filtered_cv_map):
    perms = {''.join(i) for i in permutations(name)}
    print('{}共有{}种组合'.format(name,len(perms)),file=sys.stderr)
    vowels = 'aeiou'
    filter_1 = set()
    for candidate in perms:
        temp = ''
        for letter in candidate:
            if letter in vowels:
                temp += 'v'
            else:
                temp += 'c'
        if temp in filtered_cv_map:
            filter_1.add(candidate)
    print('当前筛选出{}的cv组合共计{}个'.format(name,len(filter_1)),file=sys.stderr)
    return filter_1

'''
#5.使用三元过滤从#4结果中筛选
        定义一个空集合保存需要删除的数据
        遍历#4的结果
            遍历三元组过滤表的结果
            过滤数据小写化
            过滤数据与已有数据对比，并保存
        删除无用数据
'''

def trigram_filter(filter_1,trigram_filtered):
    filtered = set()
    for candidate in filter_1:
        for triplet in trigram_filtered:
            triplet = triplet.lower()
            if triplet == candidate:
                filtered.add(candidate)
    filter_2 = filter_1 - filtered
    print('再次筛选出数据{}条'.format(len(filter_2)),file=sys.stderr)
    return filter_2

'''
#6.在#5原有基础上再次二元组过滤
    定义一个空集合储存错误结果
    定义不可能出现的字母对
    定义不可能在单词开头出现的字母对
    遍历输出三元组筛选数据
        判断数据是否含有不可能出现的字母对
    删除错误结果
    如果定义的值在字母对中提示无效
'''

def letter_pair_filter(filter_2):
    filtered = set()
    rejects = ['dt','lr','md','ml','mr','mt','mv','td','tv','vd','v1','vm','vr','vt']
    first_pair_rejects = ['ld','lm','lt','lv','rd','rl','rm','rt','rv','tl','tm']
    for candidate in filter_2:
        for r in rejects:
            if r in candidate:
                filtered.add(candidate)
        for fp in first_pair_rejects:
            if candidate.startswith(fp): #startswith()函数方法用于检查字符串是否是以指定子字符串开头，如果是则返回 True，否则返回 False
                filtered.add(candidate)
    filter_3 = filter_2 - filtered
    print('共筛选出{}条结果'.format(len(filter_3)),file=sys.stderr)
    if 'voldemort' in filter_3:
        print('voldemort不可用',file=sys.stderr)
    return filter_3

'''
#7.用户选择字母开头
    输入开头字母
    创建空列表存放数据
    遍历二元组筛选数据
    判断输入书否有效，并保存
'''

def view_by_letter(name,filter_3):
    print('剩余{}个字母'.format(len(name)),file=sys.stderr)
    first = input('请输入易位查询字母: ')
    subset = []
    for candidate in filter_3:
        if candidate.startswith(first):
            subset.append(candidate)
    print(*sorted(subset),sep='\n')
    print('以{}开始选择的结果有{}条'.format(first,len(subset)),file=sys.stderr)
    try_again = input('按任意键退出/按#继续: ')
    if try_again == '#':
        view_by_letter(name,filter_3)
    else:
        sys.exit()

def main():
    name = 'tmvoordle'

    name = name.lower()

    word_list_ini = load.load_file('2of12inf.txt')
    trigrams_filtered = load.load_file('least-likely_trigrams.txt')

    word_list = prep_words(name,word_list_ini) #统计name与单词长度

    filtered_cv_map = cv_map_words(word_list) #使用cv过滤法排除不合格的单词

    filter_1 = cv_map_filter(name,filtered_cv_map) #cv模式化name与#3作比较

    filter_2 = trigram_filter(filter_1,trigrams_filtered) #使用三元过滤从#4结果中筛选

    filter_3 = letter_pair_filter(filter_2) #在#5原有基础上再次二元组过滤

    view_by_letter(name,filter_3)

if __name__ == '__main__':
    main()